package com.starxg.m3u8.controller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import com.starxg.m3u8.config.ApplicationConfig;
import com.starxg.m3u8.download.*;
import com.starxg.m3u8.exception.FileDownloadUrlException;
import com.starxg.m3u8.exception.M3U8Exception;
import com.starxg.m3u8.gui.LoadingDialog;
import com.starxg.m3u8.gui.M3U8TableView;
import com.starxg.m3u8.gui.TopMenuBar;
import com.starxg.m3u8.parser.*;

import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * MainController
 *
 * @author huangxingguang@lvmama.com
 * @date 2020-08-12 22:57
 */
@Slf4j
@Getter
public class MainController extends BaseController {

    @FXML
    private VBox control;
    @FXML
    private ControlController controlController;
    @FXML
    private TopMenuBar menuBar;
    @FXML
    private M3U8TableView table;
    @FXML
    private Label lblStatus;
    private M3U8Download m3u8Download;

    /**
     * 是否获取中
     */
    private SimpleBooleanProperty isGetting = new SimpleBooleanProperty(false);

    /**
     * 是否下载中
     */
    private SimpleBooleanProperty isDownloading = new SimpleBooleanProperty(false);

    /**
     * 是否合并中
     */
    private SimpleBooleanProperty isMerging = new SimpleBooleanProperty(false);

    @Override
    public void initView() {
        menuBar.init(this);
        table.init(this);
        controlController.init(this);
    }

    @Override
    public void initEvent() {

        controlController.setAnalysisAction(this::analysis);

        controlController.setDownloadAction((file) -> {
            if (table.getItems().isEmpty()) {
                return;
            }

            M3U8 m3u8 = (M3U8) table.getUserData();
            try (var out = new FileOutputStream(
                    new File(file.getParent(), FilenameUtils.getBaseName(file.getName()) + ".m3u8"))) {
                IOUtils.write(m3u8.getRaw(), out, StandardCharsets.UTF_8);
            } catch (IOException e) {
                log.warn("保存m3u8文件失败。原因:{}", e.getMessage());
            }
            m3u8TextDownload(m3u8, table.getItems(), file);

        });

        controlController.setPauseAction(() -> {
            if (m3u8Download != null) {
                table.getItems().forEach(e -> {
                    if (e.getStatus() != M3U8ItemStatus.SUCCEEDED && e.getStatus() != M3U8ItemStatus.FAILED) {
                        e.setStatus(M3U8ItemStatus.PAUSED);
                    }
                });
                m3u8Download.stop();
            }
        });

        lblStatus.textProperty().addListener((observable, oldValue, newValue) -> log.info(newValue));
    }

    // region 解析地址

    /**
     * 解析地址
     */
    private void analysis(URL url) {
        M3U8Analysis analysis;

        if (url.getProtocol().startsWith("file")) {

            String m3u8Url = prompt("请输入M3U8的下载地址", StringUtils.EMPTY);
            if (StringUtils.isBlank(m3u8Url)) {
                return;
            }

            String file = url.getFile();

            try {
                url = new URL(m3u8Url);
            } catch (MalformedURLException e) {
                alert("非法的URL", Alert.AlertType.ERROR);
                return;
            }

            analysis = new M3U8Analysis(url, new File(file));

        } else {
            analysis = new M3U8Analysis(url);
        }

        final URL finalUrl = url;
        analysis.start(new M3U8AnalysisCallback() {
            @Override
            public void before() {
                isGetting.set(true);
                log.info("开始解析地址：{}", finalUrl);
                lblStatus.setText("解析地址...");
            }

            @Override
            public void failed(Throwable e) {
                log.error(e.getMessage(), e);
                if (e instanceof FileDownloadUrlException) {
                    if (confirm(e.getMessage())) {
                        openDocument("https://www.baidu.com/");
                    }
                } else {
                    alert(e.getMessage(), Alert.AlertType.ERROR);
                    lblStatus.setText("解析地址失败。原因：" + e.getMessage());
                }

            }

            @Override
            public void succeeded(M3U8 m3u8) {
                analysis(m3u8);
            }

            @Override
            public void completed() {
                isGetting.set(false);
            }
        });
    }

    /**
     * 解析m3u8
     */
    private void analysis(M3U8 m3u8) {
        if (m3u8 instanceof M3U8Multi) {
            ChoiceDialog<M3U8Multi.Stream> dialog = new ChoiceDialog<>(null, ((M3U8Multi) m3u8).getStreams());
            dialog.setTitle("提示");
            dialog.setHeaderText(null);
            dialog.setContentText("请选择要下载的码率");
            dialog.initOwner(getStage());
            Optional<M3U8Multi.Stream> optional = dialog.showAndWait();
            if (optional.isEmpty()) {
                lblStatus.setText("就绪");
                return;
            }
            analysis(optional.get().getUrl());
        } else if (m3u8 instanceof M3U8Text) {
            Collection<M3U8Item> collection = ((M3U8Text) m3u8).getItems();
            lblStatus.setText("解析地址成功。共 " + collection.size() + " 条");
            table.getItems().addAll(collection);
            table.setUserData(m3u8);
        } else if (m3u8 instanceof M3U8Live) {
            throw new M3U8Exception("暂不支持直播流下载");
        } else {
            throw new M3U8Exception("无法识别：" + m3u8.getClass());
        }
    }

    // endregion

    // region 下载普通的m3u8

    private void m3u8TextDownload(M3U8 m3u8, List<M3U8Item> items, File file) {
        m3u8Download = new M3U8Download(items, file, new M3U8DownloadCallback<>() {
            private M3U8Download download;
            private volatile long prevTime;
            private Lock lock;

            @Override
            public void before(M3U8Download m3u8Download) {
                this.download = m3u8Download;
                this.prevTime = System.currentTimeMillis();
                this.lock = new ReentrantLock();
                isDownloading.set(true);
                lblStatus.setText("下载中 " + m3u8Download.getSucceeded() + " / " + items.size());
            }

            @Override
            public void part(M3U8Item item) {
                item.setStatus(M3U8ItemStatus.SUCCEEDED);
                lblStatus.setText("下载中 " + download.getSucceeded() + " / " + items.size());
            }

            @Override
            public void part(M3U8Item item, Throwable e) {
                log.error("分片 [{}] 下载失败。 原因:{}", item.getName(), e.getMessage(), e);
                item.setStatus(M3U8ItemStatus.FAILED);
                item.setError(e);
            }

            @Override
            public void stopped() {
                lblStatus.setText("下载暂停，点击 “下载” 可继续。");
            }

            @Override
            public void process(M3U8Item item, long total, long read, double process) {
                item.setProgress(process * 100);
                long now = System.currentTimeMillis();

                // 一秒刷新一次
                if (now - 2000 >= prevTime) {
                    if (lock.tryLock()) {
                        try {
                            prevTime = now;
                            Platform.runLater(() -> table.refresh());
                        } finally {
                            lock.unlock();
                        }
                    }
                }
            }

            @Override
            public void succeeded() {
                if (ApplicationConfig.getInstance().getApp().isAutoMergeTs()
                        || confirm("下载完成，是否合并分片？（你也可以稍后再次点击下载合并）")) {
                    mergeTs(m3u8, items, file);
                } else {
                    lblStatus.setText("你可以再次点击 “下载” 进行合并。");
                }
            }

            @Override
            public void completed() {
                log.info("下载结束。成功：{}，失败：{}", download.getSucceeded(), items.size() - download.getSucceeded());
                isDownloading.set(false);
                table.refresh();
            }

        }).start();
    }

    // endregion

    /**
     * 合并分片
     */
    private void mergeTs(M3U8 m3u8, List<M3U8Item> items, File savePath) {
        new M3U8Merger(m3u8, items, savePath).start(new M3U8DownloadCallback<>() {
            private LoadingDialog dialog;

            @Override
            public void before(M3U8Merger before) {
                lblStatus.setText("合并分片中");
                dialog = new LoadingDialog("合并中");
                dialog.initOwner(getStage());
                dialog.show();
            }

            @Override
            public void part(M3U8Item item, Throwable e) {
                log.warn("分片 [{}] 合并失败 , 原因：{}", item.getName(), e.getMessage());
            }

            @Override
            public void completed() {
                Platform.runLater(() -> {
                    dialog.closeNow();
                    lblStatus.setText("合并完成，文件地址：" + savePath.getAbsolutePath());
                    controlController.getBtnDownload().setDisable(true);
                    controlController.getBtnPause().setDisable(true);
                    if (confirm("合并分片完成，是否打开合并后的文件目录？")) {
                        openFinderLightFile(savePath);
                    }
                });
            }

            @Override
            public void part(M3U8Item item) {
                log.info("分片 [{}] 合并成功", item.getName());
            }

        });
    }

}
